vue@3学习

本文最后更新于:2022年5月10日 下午

VUE3带来的好东西

Proxy API

vue2.X存在的缺陷

  • 无法检测对象中 property 的添加或移除;
  • 通过下标操作数组不会触发响应 ,vm.items[indexOfItem] = newValue能做到但性能考虑放弃;
  • 修改数组的长度时不会触发响应式,vm.items.length = newLength
  • vue2时代的解决方法
    // Object新增元素
    Vue.set(object, propertyName, value)
    // or
    this.$set(this.someObject,'b',2)
    
    
    // 下标修改数组
    Vue.set(vm.array1, indexOfItem, newValue)
    // or
    this.$set(this.array1, indexOfItem, newValue)
    // or
    this.array1.splice(indexOfItem, 1, newValue)
    
    
    // 修改数组长度
    this.array1.splice(newLength)
  • 重写了数组部分方法以支持响应式
    pop()
    push()
    shift()
    unshift()
    splice()
    sort()
    reverse()

    defineProperty

Object.defineProperty(obj/* 对象*/, prop/* 属性*/, descriptor/*描述符*/){
  //对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
  //数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
  //存取描述符是由 getter 函数和 setter 函数所描述的属性。
  //一个描述符只能是这两者其中之一;不能同时是两者。

  configurable: true,
  //configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。
  enumerable: true,
  //enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
  value: true,
  writable: true,
  //当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。
  //
  // 或者
  //
  get() { ... },
  set(newValue) { ... },
  enumerable : true,
  configurable : true
}
  1. 兼容IE9
  2. 劫持了对象上的属性
  3. 数据结构越复杂初始性能会越差,需要递归遍历绑定defineProperty
  4. 为保持响应式还需要为新增数据再次绑定defineProperty
  5. 需要对原始数据修改来触发拦截器

proxy

const p = new Proxy(target, handler)
handler.getPrototypeOf()
//Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf()
//Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible()
//Object.isExtensible 方法的捕捉器。
handler.preventExtensions()
//Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor()
//Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.defineProperty()
//Object.defineProperty 方法的捕捉器。
handler.has()
//in 操作符的捕捉器。
handler.get()
//属性读取操作的捕捉器。
handler.set()
//属性设置操作的捕捉器。
handler.deleteProperty()
//delete 操作符的捕捉器。
handler.ownKeys()
//Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
handler.apply()
//函数调用操作的捕捉器。
handler.construct()
//new 操作符的捕捉器。
  1. 不兼容IE
  2. 劫持整个对象,无需对对象本体作出修改
  3. 响应数组的改变
  4. 拦截器响应的是proxy生成的代理对象,原数据的修改将不会触发拦截器
  5. 拦截器种类繁多

组合式API生命周期

onXXXX

beforeCreate + created => setup
beforeMount => onBeforeMount
mounted => onMounted
beforeUpdate => onBeforeUpdate
updated => onUpdated
beforeDestroy => onBeforeUnmount
destroyed => onUnmounted

// keep-live组件
activated => onActivated
deactivated => onDeactivated

// 错误捕获
oneErrorCaptured

// 追踪
onRenderTracked
onRenderTriggered

Composition API

组合式api,解决关注点分离问题,同一逻辑代码可以摆放在一起

Hooks

css in js

自定义指令

自定钩子与组件生命周期统一

created() // 在元素的 attribute 或事件监听器被应用之前调用。
beforeMount()
mounted()
beforeUpdate()
updated()
beforeUnmount()
unmounted()

const app = Vue.createApp({})

app.directive('highlight', {
  beforeMount(el, binding, vnode) {
    el.style.background = binding.value
  }
})

简单实现

实现reactive

// reactivity.js 猴版响应式框架
const targetMap = new WeakMap();
const effectStack = [];
const isObject = obj => obj !== null && typeof obj === "object";

const baseHandler = {
  get(target, key) {
    const ret = Reflect.get(target, key);
    track(target, key);
    if (typeof ret === "object") {
      return reactive(ret);
    }
    return ret;
  },
  set(target, key, val) {
    Reflect.set(target, key, val);
    trigger(target, key, val);
  },
};

function track(target, key) {
  let activeEffect = effectStack[effectStack.length - 1];
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect);
    // TODO
    activeEffect.deps.push(dep);
  }
}

function trigger(target, key, val) {
  const depsMap = targetMap.get(target);
  if (!depsMap) {
    // never been tracked
    return;
  }
  const effects = new Set();
  if (key) {
    const dep = depsMap.get(key);
    dep.forEach((effect) => {
      effects.add(effect);
    });
  }
  effects.forEach((ef) => ef());
}

function createReactiveEffect(fn,options){
  const effect = function reactiveEffect(){
      try {
        effectStack.push(effect)
        return fn()
      } finally {
        effectStack.pop()
      }
  }
  effect.deps = []
  effect.options = options
  return effect
}

// ---------

function reactive(target) {
  return new Proxy(target, baseHandler);
}


function effect(fn, options={}) {
  const effect = createReactiveEffect(fn, options);
  if (!options.lazy) {
    effect();
  }
  return effect;
}

function computed(fn){
  const runner = effect(fn,{computed:true,lazy:true})
  return {
    effect:runner,
    get val(){
      return runner()
    }
  }
}

简单实现vuex

import {reactive,inject,provide} from 'vue'

const STORE_KEY = Symbol()

function useStore(){
	return reject(STORE_KEY)
}

function createStore(options){
	return new Store(options)
}

class Store{
	constructor(options){
		const {state,mutations} = options
		this._state = reactive({
			data:state
		})
		this._mutations = mutations
	}

	get state(){
		return this._state.data
	}

	commit(type,payload){
		const entry = this._mutations[type]
		entry && entry(this.state,payload)
	}

	install(app){
		app.provide(STORE_KEY,this)
	}
}
export {createStore,useStore}

vue@3学习
http://yoursite.com/2022/02/24/[源码]vue3/
作者
tatekii
发布于
2022年2月24日
许可协议